/***************************************************************************
 *   This is a part of the AMCAX Web CAD.                                  *
 *   Copyright (C) AMCAX, All rights reserved.                             *
 ***************************************************************************/

export default class WsTunnel {
  private serverBaseURL: string | URL;
  private name: string;
  private ws: WebSocket = null;
  private sent: number = 0;
  private recv: number = 0;
  private observers: any[] = [];
  private observersStatus: any[] = [];

  constructor(name: string, wsaddr: string | URL) {
    this.serverBaseURL = wsaddr;
    this.name = name;
    this.ws = null;
    this.sent = 0;
    this.recv = 0;
    this.observers = [];
    this.observersStatus = [];
  }

  _clear() {
    this.ws.onopen = null;
    this.ws.onclose = null;
    this.ws.onerror = null;
    this.observers = [];
    this.observersStatus = [];
  }

  attach(target: any, callback: any) {
    const targetObs =
      target === 'status' ? this.observersStatus : this.observers;
    targetObs.push(callback);
  }

  detach(target: any, callback: any) {
    const targetObs =
      target === 'status' ? this.observersStatus : this.observers;
    for (let i = targetObs.length - 1; i >= 0; i--) {
      if (targetObs[i] === callback) {
        targetObs[i] = undefined;
      }
    }
  }

  broadcast(target: any, data: any) {
    const targetObs =
      target === 'status' ? this.observersStatus : this.observers;
    try {
      for (let i = 0; i < targetObs.length; i++) {
        targetObs[i](data);
      }
    } catch (e) {
      console.error('WsTunnel Error while broadcast call:', e);
    }
  }

  open() {
    // console.log(`WsTunnel ${this.name} connecting to ${this.serverBaseURL}`)
    return new Promise<void>((resolve, reject) => {
      this.ws = new WebSocket(this.serverBaseURL);
      this.ws.onopen = () => {
        resolve();

        this.ws.onmessage = (evt) => {
          this.broadcast('data', evt.data);
        };

        this.ws.onclose = () => {
          // console.log(`WsTunnel ${this.name} closed`)
          this.broadcast('status', false);
        };

        this.broadcast('status', true);
      };
      this.ws.onclose = function () {
        // console.log(`WsTunnel ${this.name} open fail`)
        reject();
      };
      this.ws.onerror = function (evt) {
        reject(evt);
      };
    });
  }

  get isConnecting() {
    return this.ws?.readyState === WebSocket.CONNECTING;
  }

  get isOpen() {
    return this.ws?.readyState === WebSocket.OPEN;
  }

  close() {
    if (this.ws) {
      this.ws.close();
      this.ws = null;
      this.observers = [];
    }
  }

  send(msg: string | ArrayBuffer | Blob | ArrayBufferView): Promise<void> {
    return new Promise((resolve, reject) => {
      if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
        reject('WsTunnel WebSocket is not initialized or not open');
        return;
      }
      try {
        this.ws.send(msg);
        resolve(); // 消息发送成功
      } catch (error) {
        reject(error); // 捕获发送过程中的错误
      }
    });
  }
}
